设计模式(五)工厂模式 & 装饰者模式
工厂模式
应用场景
创建不同类型的披萨对象
方案一
直接new出具体类型的对象
public class PizzaStore1 {
Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
方案二
将具体(直接耦合、实现)的、会变化的创建对象部分抽离,包装进一个类,这常被称为“静态工厂”(或叫“简单工厂”)。它的缺点是不能通过继承来改变创建方法的行为。
public class PizzaStore2 {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
}
return pizza;
}
}
通过方案二封装出来的创建披萨简单工厂,将具体的实现解耦出来只依赖接口了,其它分店也可以复用了,修改要创建的类型时也更封闭。
假若PizzaStore开始搞加盟店,不同的加盟店会根据当地的口味调整提供Pizza的种类,在简单工厂中再根据多一个地区标识去判断就会越来越复杂,耦合越来越严重。
方案三
使用“工厂方法”,来监控创建的披萨质量(当然还是可以继承方案二中的简单工厂,提供不同的子类工厂给PizzaStore来创建,这也是工厂方法,但PizzaStore就无法过多地干预创建的过程)
public abstract class PizzaStore3 {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}
public class NYPizzaStore3 extends PizzaStore3 {
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (type.equals("clam")) {
return new NYStyleClamPizza();
}
}
}
public class ChicagoPizzaStore3 extends PizzaStore3 {
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (type.equals("clam")) {
return new ChicagoStyleClamPizza();
}
}
}
工厂方法(模式)通过让子类决定该创建的对象是什么,达到对象创建的过程封装的目的。
这里还涉及一个涉及原则“反依赖倒置原则”,可以按下面三点方针去遵循:1)变量不可以持有具体类的引用;2)不要让类派生自具体类;3)不要覆盖基类中已实现的方法。
上面的方案二和方案三中的工厂或者工厂方法中,都还是依赖了具体类(即 NY or Chicago 这两个地区种类的披萨),并不符合“反依赖倒置原则”,若未来除cheese和clam外还多了3种披萨,那这两个地区的披萨总和就从4个增至10个
方案四
优化工厂中的依赖关系
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
}
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
}
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThickCrustDough();
}
public Sauce createSauce() {
return new PlumTomatoSauce();
}
}
public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
abstract void prepare();
//... 其它方法
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
//抽象工厂在此起关键作用了
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
}
//... 其它方法
}
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
}
//... 其它方法
}
public class NYPizzaStore4 extends PizzaStore3 {
protected Pizza createPizza(String type) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (type.equals("cheese")) {
//(间接)应用抽象工厂(传给产品来使用)
pizza = new CheesePizza(ingredientFactory);
} else if (type.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
}
return pizza;
}
}
public class ChicagoPizzaStore4 extends PizzaStore3 {
protected Pizza createPizza(String type) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
if (type.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
} else if (type.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
}
return pizza;
}
}
这个就是“抽象工厂模式”,通过采取对象的组合的方式实现(工厂与产品组合,产品使用不同的工厂创建配件族),现在即使披萨的基本种类增至5个,也不至于要另外为2个不同地区同时新增6(2x3)种地区性披萨,也就是说类的增量只有基本种类的3个新披萨。
同时可以看到,NYPizzaIngredientFactory
和ChicagoPizzaIngredientFactory
里其实都用到了“工厂方法模式”,只是方法不止一个,而是包括了创建这个产品的家族的多个方法。
总结
工厂方法模式是定义出一个创建对象的接口,但由子类决定要实例化的类是哪一个,让类把实例化推迟到子类。抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
装饰者模式
应用场景
计算有n种调料可添加的饮品的价格
方案一
为每种调料都创建一个对应的布尔值,用于在计价时判断是否算上对应调料的价格。
此方案就不贴代码出来了,因为太多的if-else,没什么技术含量。(若不使用该方案,也应该不至于对每个调料创建一个子类来解决if-else多的问题吧)
方案二
将不同的调料定为不同的装饰者,将饮品定为被装饰者,根据需要可包裹多层的装饰(即添加多种调料)
public abstract class Beverage { //当然也可以是接口
String description = "Unknown beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
//基础饮品(第一层被装饰的)
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "HouseBlend";
}
public double cost() {
return 0.89;
}
}
//调料(/饮品,第n层被装饰的,也是装饰第n-1层的)
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return 0.20 + beverage.cost();
}
}
装饰者可以在被装饰者的行为前面或/与后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
可以使用无数个装饰者去包装一个组件,因为装饰者一般对组件的客户是透明的,除非客户依赖的组件是具体类型,当然简单的只包一层也是可以的(不一定要使用上面例子中的嵌套同类的实例对象在类中)。
总结
装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。这是一个符合对扩展开放,对修改关闭原则的模式。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mingfungliu@gmail.com
文章标题:设计模式(五)工厂模式 & 装饰者模式
文章字数:1.8k
本文作者:Mingfung
发布时间:2019-02-12, 11:54:00
最后更新:2019-02-12, 14:34:30
原始链接:http://blog.ifungfay.com/设计模式/设计模式(五)工厂模式 & 装饰者模式/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。